use std::sync::{Condvar, Mutex};

use super::SiminkManage;

/// simink 退出原因
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SiminkQuitCause {
    /// 默认状态
    None,
    /// 日志记录致命错误
    LogRecordFaltal,
    /// 主机错误
    HostError,
    /// 主机监视器退出
    HostMonitorQuit,
    /// 主机监视器被外部事件中断(CtrlC|CtrlD|CtrlA+x)
    HostMonitorInterrupted,
    /// 客户机关机
    GuestShutdown,
    /// 客户机 panic
    GuestPanic,
}

/// simink 请求
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SiminkRequest {
    /// 没有请求
    ReqNone,
    /// 请求主机退出
    ReqHostExit,
}

/// simink 运行状态
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ManageSiminkStatus {
    /// 系统启动中
    Booting,
    /// 处理事件中
    Handling,
    /// 等待事件中
    Waiting,
    /// 系统退出中
    Quiting,
}

pub(crate) struct ManageSimink {
    quit_cause: Mutex<SiminkQuitCause>,
    request: Mutex<SiminkRequest>,
    status: Mutex<ManageSiminkStatus>,
    kick: Condvar,
}

impl ManageSimink {
    pub(crate) fn new() -> Self {
        Self {
            quit_cause: Mutex::new(SiminkQuitCause::None),
            request: Mutex::new(SiminkRequest::ReqNone),
            status: Mutex::new(ManageSiminkStatus::Booting),
            kick: Condvar::new(),
        }
    }
}

impl SiminkManage {
    /// 获取 simink 运行状态
    #[allow(clippy::missing_panics_doc)]
    pub fn simink_status(&self) -> ManageSiminkStatus {
        let lock = self.simink.status.lock().unwrap();
        *lock
    }

    fn set_simink_status(&self, status: ManageSiminkStatus) {
        let mut lock = self.simink.status.lock().unwrap();
        *lock = status;
    }

    /// simink 等待请求事件
    ///
    /// 该函数会自动维护 simink 状态, 首先将状态设置为等待请求状态并进入睡眠等待请求,
    /// 当有请求唤醒后, 设置 simink 状态 handling, 程序应该主动开始处理事件
    #[allow(clippy::missing_panics_doc)]
    pub fn simink_wait_request(&self) -> SiminkRequest {
        self.set_simink_status(ManageSiminkStatus::Waiting);
        let mut lock = self.simink.request.lock().unwrap();
        while *lock == SiminkRequest::ReqNone {
            lock = self.simink.kick.wait(lock).unwrap();
        }
        self.set_simink_status(ManageSiminkStatus::Handling);
        *lock
    }

    /// 设置 simink 为退出状态
    ///
    /// 设置该状态后, 其他工作线程应该主动检测到该状态并清理自身资源退出工作线程,
    /// 以便于主程序可以正确的调用`worker_join`等待所有工作线程退出
    pub fn set_simink_quit(&self) {
        self.set_simink_status(ManageSiminkStatus::Quiting);
    }
}

impl SiminkManage {
    // 唤醒 simink
    fn simink_kick(&self) {
        self.simink.kick.notify_one();
    }

    /// 请求 simink 退出
    #[allow(clippy::missing_panics_doc)]
    pub fn request_simink_quit(&self, reason: SiminkQuitCause) {
        {
            let mut lock = self.simink.quit_cause.lock().unwrap();
            *lock = reason;
        }
        {
            let mut lock = self.simink.request.lock().unwrap();
            *lock = SiminkRequest::ReqHostExit;
        }
        self.simink_kick();
    }
}

impl SiminkManage {
    /// 获取当前 simink 的请求状态
    #[allow(clippy::missing_panics_doc)]
    pub fn simink_request_status(&self) -> SiminkRequest {
        *self.simink.request.lock().unwrap()
    }

    /// 获取请求 simink 退出的原因
    #[allow(clippy::missing_panics_doc)]
    pub fn simink_quit_cause(&self) -> SiminkQuitCause {
        *self.simink.quit_cause.lock().unwrap()
    }
}
